package fileGenerate

import (
	"dbGenerate/dbRead"
	"fmt"
	"os"
	"strings"
	"time"
)

type FileGenC struct {
	structname string
}

func (f *FileGenC) generateFuncStruct(info dbRead.DBTableInfo, property FileGenProperty) string {
	structname :=  property.Prefix + "_" + property.SqlType + "_" + property.Table + "_" + "info"
	f.structname = structname
	structstr := "typedef struct {\n"
	for k, v := range info.KeyMap {
		if k == "char *" {
			structstr += "\t" + k + " " + v + "[1024];\n"
		} else {
			structstr += "\t" + k + "  " + v + "\n"
		}
	}
	structstr += "} " + structname + ";\n\n"
	return structstr
}

func (f *FileGenC) generateFuncQuery(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFuncQuery := "int " + properties.Prefix + "_" + properties.SqlType + "_" + properties.Table + "_query(" +
		"sqlite3 *db, const char *sql," + f.structname + " *result"
	sFuncQuery += ")"
	return sFuncQuery
}

func (f *FileGenC) generateFuncInsert(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFuncQuery := "int " + properties.Prefix + "_" + properties.SqlType + "_" + properties.Table + "_insert(" +
		"sqlite3 *db, const char *sql," + f.structname + " *result"
	sFuncQuery += ")"
	return sFuncQuery
}

func (f *FileGenC) generateFuncUpdate(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFuncQuery := "/// 哪几个 key 是NULL， 哪几个就是主键，数据库根据这几个查询到，然后把其它数据插入\n"
	sFuncQuery += "/// 如果程序可以自动获取主键是最好，直接使用主键查询\n"
	sFuncQuery += "int " + properties.Prefix + "_" + properties.SqlType + "_" + properties.Table + "_update(" +
		"sqlite3 *db, const char *sql," + f.structname + " *result"
	sFuncQuery += ")"
	return sFuncQuery
}

func (f *FileGenC) generateFuncDelete(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFuncQuery := "int " + properties.Prefix + "_" + properties.SqlType + "_" + properties.Table + "_delete(" +
		"sqlite3 *db, const char *sql," + f.structname + " *result"
	sFuncQuery += ")"
	return sFuncQuery
}

func (f *FileGenC) generateFuncHeader(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFuncQuery := f.generateFuncStruct(info, properties)
	// generate query
	sFuncQuery += f.generateFuncQuery(info, properties)
	sFuncQuery += ";\n\n"

	sFuncQuery += f.generateFuncInsert(info, properties)
	sFuncQuery += ";\n\n"

	sFuncQuery += f.generateFuncUpdate(info, properties)
	sFuncQuery += ";\n\n"

	sFuncQuery += f.generateFuncDelete(info, properties)
	sFuncQuery += ";\n\n"

	return sFuncQuery
}

func (f *FileGenC) generateFuncImplQuery(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFunc := f.generateFuncQuery(info, properties)
	sFunc += "\n{\n"
	sFunc += "    return 0;\n"
	sFunc += "}\n"
	return sFunc
}

func (f *FileGenC) generateFuncImplInsert(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFunc := f.generateFuncInsert(info, properties)
	sFunc += "\n{\n"
	sFunc += "    return 0;\n"
	sFunc += "}\n"
	return sFunc
}

func (f *FileGenC) generateFuncImplUpdate(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFunc := f.generateFuncUpdate(info, properties)
	sFunc += "\n{\n"
	sFunc += "    return 0;\n"
	sFunc += "}\n"
	return sFunc
}

func (f *FileGenC) generateFuncImplDelete(info dbRead.DBTableInfo, properties FileGenProperty) string {
	sFunc := f.generateFuncDelete(info, properties)
	sFunc += "\n{\n"
	sFunc += "    return 0;\n"
	sFunc += "}\n"
	return sFunc
}

func (f *FileGenC) generateFuncImpl(info dbRead.DBTableInfo, properties FileGenProperty) string {
	// generate
	sFuncQuery := f.generateFuncImplQuery(info, properties)
	sFuncQuery += "\n"

	sFuncQuery += f.generateFuncImplInsert(info, properties)
	sFuncQuery += "\n"

	sFuncQuery += f.generateFuncImplUpdate(info, properties)
	sFuncQuery += "\n"

	sFuncQuery += f.generateFuncImplDelete(info, properties)
	sFuncQuery += "\n"

	return sFuncQuery
}

func (f *FileGenC) GenerateHeader(info dbRead.DBTableInfo, property FileGenProperty) error {
	filepath := property.OutPos + "/" + property.Prefix + "_" + property.SqlType + "_" + property.Table + ".h"
	fmt.Println("filename ", filepath)

	fr, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
	if err != nil {
		return err
	}

	sHeader := "/**\n" +
		" * @filename %s\n" +
		" * @date %s\n" +
		" * This file is generated by golang\n" +
		" */\n\n"
	sHeader = fmt.Sprintf(sHeader, filepath, time.Now().String())
	n, err := fr.WriteString(sHeader)
	if err != nil {
		fmt.Println("write fail ", err)
	} else {
		fmt.Println("write done len ", n)
	}

	// predefine
	sHeaderDefine := "#ifndef " + strings.ToUpper(property.Prefix) + "_" +
		strings.ToUpper(property.SqlType) + "_" + strings.ToUpper(property.Table) + "_H\n"
	sHeaderDefine += "#define " + strings.ToUpper(property.Prefix) + "_" +
		strings.ToUpper(property.SqlType) + "_" + strings.ToUpper(property.Table) + "_H\n\n"
	fr.WriteString(sHeaderDefine)

	sHeaderExtern := "#ifdef __cplusplus\n"
	sHeaderExtern += "extern \"C\" {\n"
	sHeaderExtern += "#endif\n\n"
	fr.WriteString(sHeaderExtern)

	sHeaderContent := f.generateFuncHeader(info, property)
	fr.WriteString(sHeaderContent)

	sHeaderExternLast := "#ifdef __cplusplus\n"
	sHeaderExternLast += "}\n"
	sHeaderExternLast += "#endif\n\n"
	fr.WriteString(sHeaderExternLast)

	sHeaderDefineLast := "#endif\n\n"
	fr.WriteString(sHeaderDefineLast)

	fr.Close()
	return nil
}

func (f *FileGenC) GenerateImpl(info dbRead.DBTableInfo, property FileGenProperty) error {
	filepath_pre := property.OutPos + "/" + property.Prefix + "_" + property.SqlType + "_" + property.Table
	filepath := filepath_pre + ".c"
	fmt.Println("filename ", filepath)

	fr, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
	if err != nil {
		return err
	}

	sHeader := "/**\n" +
		" * @filename %s\n" +
		" * @date %s\n" +
		" * This file is generated by golang\n" +
		" */\n\n"
	sHeader = fmt.Sprintf(sHeader, filepath, time.Now().String())
	n, err := fr.WriteString(sHeader)
	if err != nil {
		fmt.Println("write fail ", err)
	} else {
		fmt.Println("write done len ", n)
	}

	// predefine
	sHeaderDefine := "#include <stdio.h>\n"
	sHeaderDefine += "#include <sqlite3.h>\n"
	sHeaderDefine += "#include \"" + filepath_pre + ".h\"\n\n"

	fr.WriteString(sHeaderDefine)

	sHeaderContent := f.generateFuncImpl(info, property)
	fr.WriteString(sHeaderContent)

	fr.Close()
	return nil
}
